
// import haxe.ds.Array;
// import oimo.m.Vec3;
// import oimo.m.M;
// import oimo.common.MathUtil;
// import oimo.common.Vec3;

import { EpaVertex } from "./EpaVertex";
import { Vec3 } from "../../../../common/Vec3";
import { M } from "../../../../common/M";
import { GjkEpaLog } from "./GjkEpaLog";
import { SimplexUtil } from "./SimplexUtil";

/**
 * numberernal class.
 */


export class EpaTriangle {
	_next: EpaTriangle;
	_prev: EpaTriangle;

	_vertices: Array<EpaVertex>;
	_adjacentTriangles: Array<EpaTriangle>;
	_adjacentPairIndex: Array<number>;
	_normal: Vec3;
	_distanceSq: number;

	_nextIndex: Array<number>; // (0, 1, 2) -> (1, 2, 0)

	_tmpDfsId: number;
	_tmpDfsVisible: boolean;

	tmp: Vec3;

	public static count: number = 0;
	id: number;

	constructor() {
		this.id = ++EpaTriangle.count;
		this._next = null;
		this._prev = null;
		this._normal = new Vec3();
		this._distanceSq = 0;
		this._tmpDfsId = 0;
		this._tmpDfsVisible = false;

		this._vertices = new Array<EpaVertex>(3);
		this._adjacentTriangles = new Array<EpaTriangle>(3);
		this._adjacentPairIndex = new Array<number>(3);
		this.tmp = new Vec3();
		this._nextIndex = new Array<number>(3);
		this._nextIndex[0] = 1;
		this._nextIndex[1] = 2;
		this._nextIndex[2] = 0;
	}


	public checkVisible(id: number, from: Vec3): boolean {
		// if (id == _tmpDfsId) return _tmpDfsVisible;
		this.tmp.copyFrom(from).subEq(this._vertices[0].v);
		this._tmpDfsVisible = this.tmp.dot(this._normal) > 0;
		return this._tmpDfsVisible;
	}

	public init(vertex1: EpaVertex, vertex2: EpaVertex, vertex3: EpaVertex, center: Vec3, autoCheck: boolean = false): boolean {
		let v1: Vec3 = vertex1.v;
		let v2: Vec3 = vertex2.v;
		let v3: Vec3 = vertex3.v;
		let vc: Vec3 = center;
		let v12: Vec3 = v2.sub(v1); // 1 to 2
		let v13: Vec3 = v3.sub(v1); // 1 to 3
		let vc1: Vec3 = vc.sub(v1); // c to 1
		let inor: Vec3 = v12.cross(v13);
		let inverted: boolean = false;
		let d: number = M.vec3_dot(vc1, inor);
		if (d < 0) {
			if (autoCheck) {
				GjkEpaLog.log("found the triangle inverted, but it does not matter.");
				// vertices must be CCW
				let tmp = vertex2;
				vertex2 = vertex3;
				vertex3 = tmp;
				inor.negateEq();
			} else {
				GjkEpaLog.log("the triangle is inverted!");
				inverted = true;
				//return false;
			}
		}
		this._vertices[0] = vertex1;
		this._vertices[1] = vertex2;
		this._vertices[2] = vertex3;
		this._normal.copyFrom(inor);
		let tmp = new Vec3();
		SimplexUtil.projectOrigin3(vertex1.v, vertex2.v, vertex3.v, tmp);
		this._distanceSq = tmp.lengthSq();

		this._adjacentTriangles[0] = null;
		this._adjacentTriangles[1] = null;
		this._adjacentTriangles[2] = null;
		this._adjacentPairIndex[0] = -1;
		this._adjacentPairIndex[1] = -1;
		this._adjacentPairIndex[2] = -1;
		return !inverted;
	}

	public setAdjacentTriangle(triangle: EpaTriangle): boolean {
		var count: number = 0;
		for (let i = 0; i < 3; ++i) {
			for (let j = 0; j < 3; ++j) {
				var i2: number = this._nextIndex[i];
				var j2: number = this._nextIndex[j];
				if (this._vertices[i] == triangle._vertices[j2] && this._vertices[i2] == triangle._vertices[j]) {
					this._adjacentTriangles[i] = triangle;
					this._adjacentPairIndex[i] = j;
					triangle._adjacentTriangles[j] = this;
					triangle._adjacentPairIndex[j] = i;
					count++;
				}
			}
		}
		if (count != 1) {
			GjkEpaLog.log(this._vertices[0].randId + " " + this._vertices[1].randId + " " + this._vertices[2].randId);
			GjkEpaLog.log(triangle._vertices[0].randId + " " + triangle._vertices[1].randId + " " + triangle._vertices[2].randId);
			return false; // invalid polyhedron
		}
		return true;
	}

	public removeAdjacentTriangles(): void {
		for (let i = 0; i < 3; ++i) {
			var triangle: EpaTriangle = this._adjacentTriangles[i];
			if (triangle != null) {
				var pairIndex: number = this._adjacentPairIndex[i];
				triangle._adjacentTriangles[pairIndex] = null;
				triangle._adjacentPairIndex[pairIndex] = -1;
				this._adjacentTriangles[i] = null;
				this._adjacentPairIndex[i] = -1;
			}
		}
	}

	public removeReferences(): void {
		this._next = null;
		this._prev = null;
		this._tmpDfsId = 0;
		this._tmpDfsVisible = false;
		this._distanceSq = 0;
		this._vertices[0] = null;
		this._vertices[1] = null;
		this._vertices[2] = null;
		this._adjacentTriangles[0] = null;
		this._adjacentTriangles[1] = null;
		this._adjacentTriangles[2] = null;
		this._adjacentPairIndex[0] = 0;
		this._adjacentPairIndex[1] = 0;
		this._adjacentPairIndex[2] = 0;
	}

	public dump(): void {
		GjkEpaLog.log(
			`Face data: 
			this.id: ${this.id} 
			v1: ${ this._vertices[0].v} 
			v2: ${ this._vertices[1].v} 
			v3: ${ this._vertices[2].v} 
			n: ${this._normal} 
			distSq: ${this._distanceSq} `
		);
	}

}
